home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / More Source / C⁄C++ / Xconq 7.0d37 / source / x11 / xconq.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-04  |  29.9 KB  |  1,361 lines  |  [TEXT/KAHL]

  1. /* The main program of the X11 interface to Xconq.
  2.    Copyright (C) 1987, 1988, 1989, 1991, 1992, 1993, 1994, 1995
  3.    Stanley T. Shebs.
  4.  
  5. Xconq is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2, or (at your option)
  8. any later version.  See the file COPYING.  */
  9.  
  10. #include "conq.h"
  11. extern int side_acp_human PROTO ((Side *side));
  12. #include "cmdline.h"
  13. #include "xconq.h"
  14.  
  15. extern int popup_dialog;  /* from cmdline.c */
  16.  
  17. /* (Yes, we can compile all this with Think C on Macs - useful for prototype
  18.    checking) */
  19. #ifdef THINK_C
  20. char *strdup(char *);
  21. #endif
  22.  
  23. /* Local function declarations. */
  24.  
  25. void notify_instructions PROTO ((void));
  26.  
  27. static void quit_program PROTO ((Widget wdgt, XEvent *event, String *params, Cardinal *num_params));
  28.  
  29. static void run_game_proc PROTO ((XtPointer client_data, XtIntervalId *id));
  30.  
  31. static Boolean run_game_idle PROTO ((XtPointer clientdata));
  32.  
  33. static int handle_x_error PROTO ((Display *dpy, XErrorEvent *evt));
  34. static int handle_xio_error PROTO ((Display *dpy));
  35. static void handle_xt_error PROTO ((String msg));
  36.  
  37. void popup_game_dialog PROTO ((void));
  38.  
  39. /* X-specific options, plus some NULL definitions so that they'll be
  40.    ignored by X parsing and get passed through to the generic Xconq
  41.    command-line parser. */
  42.  
  43. XrmOptionDescRec xoptions[] = {
  44.     { "-background",    "*background",    XrmoptionSepArg,    NULL },
  45.     { "-bg",        "*background",    XrmoptionSepArg,    NULL },
  46.     { "-display",    ".display",    XrmoptionSepArg,    NULL },
  47.     { "-f",        NULL,        XrmoptionSkipArg,    NULL },
  48.     { "-fg",        "*foreground",    XrmoptionSepArg,    NULL },
  49.     { "-fn",        "*font",    XrmoptionSepArg,    NULL },
  50.     { "-font",        "*font",    XrmoptionSepArg,    NULL },
  51.     { "-foreground",    "*foreground",    XrmoptionSepArg,    NULL },
  52.     { "-g",        NULL,        XrmoptionSkipArg,    NULL },
  53.     { "-geometry",    "*geometry",    XrmoptionSepArg,    NULL },
  54.     { "-n",        NULL,        XrmoptionNoArg,        NULL },
  55.     { "-name",        ".name",    XrmoptionSepArg,    NULL },
  56.     { "-xrm",        NULL,        XrmoptionResArg,    NULL }
  57. };
  58.  
  59. int xoptions_count;
  60.  
  61. String fallback_resources[] = {
  62.   /* Resources for interactive setup dialogs. */
  63.   "*Setup*ChoicePopup*x: 100",
  64.   "*Setup*ChoicePopup*y: 100",
  65.  
  66.   "*Setup*ChoiceBox*label.label:    Choose a Game!",
  67.  
  68.   "*Setup*ScenarioPopup*x: 100",
  69.   "*Setup*ScenarioPopup*y: 100",
  70.  
  71.   "*Setup*ScenarioSetup*Swap.label:    Swap",
  72.   "*Setup*ScenarioSetup*Done.label:    Done",
  73.  
  74.   "*PortholeLabel.Translations: #override \\n  ~Shift<Btn1Down>: toolaction() \\n  Shift<Btn1Down>: toolselection() \\n  ~Shift<Btn2Down>: select() movelook() \\n  Shift<Btn2Down>: select() \\n  Shift<Btn2Up>:   distance() \\n  <Btn3Down>: select() moveunit() \\n  Meta<MotionNotify>: motionselect() movelook() \\n <Expose>:   redraw() \\n <KeyPress>: keypress()",
  75.  
  76.   "*ListView.height: 500",
  77.  
  78.   "*Help.width: 400",
  79.   "*Help.height: 800",
  80.  
  81.   "*helpShell*topicPort.height:    300",
  82.   "*helpShell*topicPort.width:    100",
  83.   "*helpShell*title.width:        500",
  84.   "*helpShell*title.borderWidth:      0",
  85.   "*helpShell*title.font:        -*-helvetica-bold-r-*-*-18-*-*-*-*-*-*-*",
  86.   "*helpShell*text.height:        300",
  87.   "*helpShell*text.width:        400",
  88.   "*helpShell*topicList.font:        -*-helvetica-bold-r-*-*-12-*-*-*-*-*-*-*",
  89.   "*helpShell*text*font:        7x13",
  90.  
  91.   "*PrintSetup*Dialog*value: 0",
  92.   "*PrintSetup*Dialog*value.translations: #override \\n  <Key>Return: DoDialog()",
  93.   "*PrintSetup*help.Label: \\ help ",
  94.   "*PrintSetup*print.Label:\\ print ",
  95.   "*PrintSetup*done.Label: \\ done ",
  96.   "*PrintSetup*names.label: interesting names",
  97.   "*PrintSetup*cellSize.label: cell size",
  98.   "*PrintSetup*gridSize.label: grid thick.",
  99.   "*PrintSetup*pageWidth.label: page width",
  100.   "*PrintSetup*pageHeight.label: page height",
  101.   "*PrintSetup*topMargin.label: top margin",
  102.   "*PrintSetup*bottomMargin.label: bottom margin",
  103.   "*PrintSetup*leftMargin.label: left margin",
  104.   "*PrintSetup*rightMargin.label: right margin",
  105.   "*PrintSetup*terrainGray.label: terrain gray",
  106.   "*PrintSetup*enemyGray.label: enemy gray",
  107.   "*PrintSetup*borderWidth.label: border thick.",
  108.   "*PrintSetup*connWidth.label: conn thick.",
  109.   "*PrintSetup*Help.title:   *PrintSetup help",
  110.   "*PrintSetup*Help*baseTranslations: #override \\n <Key>Return: DoneHelp() \\n <Key>Escape: DoneHelp() \\n  <Btn1Down>:  DoneHelp()",
  111.   "*PrintSetup*Help*helpDone.Label: \\ done ",
  112.  
  113.   "*OK.label: OK",
  114.   "*Cancel.label: Cancel",
  115.   NULL
  116. };
  117.  
  118. int announced = FALSE;
  119.  
  120. char *announcemsg = NULL;
  121.  
  122. int nargs;
  123. Arg tmpargs[100];
  124.  
  125. XtAppContext thisapp;
  126.  
  127. Widget thistoplevel;
  128.  
  129. Widget choiceshell = NULL;
  130.  
  131. Widget scenarioshell = NULL;
  132.  
  133. XtActionsRec quit_actions_table[] = {
  134.     { "wm-quit", quit_program },
  135. };
  136.  
  137. static void
  138. quit_program(wdgt, event, params, num_params)
  139. Widget wdgt;
  140. XEvent *event;
  141. String *params;
  142. Cardinal *num_params;
  143. {
  144.     Side *side;
  145.     Map *map;
  146.  
  147.     /* The side with toplevel matching 'wdgt' just quit. */
  148.     if (find_side_and_map_via_a_toplevel(wdgt, &side, &map)) {
  149.     if (side->ui->shell == wdgt) {
  150.         /* Go into the standard quit command. */
  151.         do_quit(side, map);
  152.      } else if (side->ui->help_shell == wdgt) {
  153.         popdown_help(side);
  154.      }
  155.     }
  156. }
  157.  
  158. /* The main program. */
  159.  
  160. int
  161. main(argc, argv)
  162. int argc;
  163. char *argv[];
  164. {
  165.     init_library_path(NULL);
  166.     printf("\n              Welcome to X11 Xconq version %s\n\n",
  167.        version_string());
  168.     printf("%s", license_string());
  169.     print_any_news();
  170.     /* Fiddle with game module structures. */
  171.     clear_game_modules();
  172.     /* Set up empty data structures. */
  173.     init_data_structures();
  174.     /* Do the usual Xt application setup. */
  175.     /* Note that this opens one display by default, which means that
  176.        later code should take note and not try to open this a second time.
  177.        Also, this will absorb all X-related arguments - anything remaining
  178.        is either a generic Xconq option or a mistake. */
  179.     xoptions_count = XtNumber(xoptions);
  180.     thistoplevel =
  181.       XtAppInitialize(&thisapp, PROGRAMCLASSNAME,
  182.               xoptions, XtNumber(xoptions), &argc, argv,
  183.               fallback_resources, (ArgList) NULL, 0);
  184.  
  185.     parse_command_line(argc, argv, general_options);
  186.  
  187.     if (popup_dialog) {
  188.     collect_possible_games();
  189.     popup_game_dialog();
  190.     }
  191.  
  192.     if (popup_dialog) {
  193.     check_player_displays();
  194.     } else {
  195.     load_all_modules();
  196.     check_game_validity();
  197.     parse_command_line(argc, argv, variant_options);
  198.     set_variants_from_options();
  199.     parse_command_line(argc, argv, player_options);
  200.     set_players_from_options();
  201.     make_trial_assignments();
  202.     check_player_displays();
  203.     /* Complain about anything that's left. */
  204.     parse_command_line(argc, argv, leftover_options);
  205.     /* (still need to merge some databases derived from display) */
  206.     }
  207.  
  208.     /* And, perform some once per application context initialization... */
  209.     XtAppAddActions(thisapp, quit_actions_table, XtNumber(quit_actions_table));
  210.     add_map_actions();
  211.     XtAppAddTimeOut(thisapp, 10, run_game_proc, NULL);
  212.     XtAppAddWorkProc(thisapp, run_game_idle, NULL);
  213.  
  214.     /* Do the time-consuming part of setup calculations. */
  215.     calculate_globals();
  216.     run_synth_methods();
  217.     final_init();
  218.     assign_players_to_sides();
  219.     print_instructions();
  220.     run_game(0);
  221.     /* Get the displays set up, but don't draw anything yet. */
  222.     init_all_displays();
  223.     /* Now bring up the init data on each display. */
  224.     init_redraws();
  225.     /* Set up the signal handlers. */
  226.     init_signal_handlers();
  227.     init_x_signal_handlers();
  228.     notify_instructions();
  229.     /* Go into the main play loop. */
  230.     XtAppMainLoop(thisapp);
  231.     /* Humor the compiler. */
  232.     return 0;
  233. }
  234.  
  235. unsigned long EffectiveMpTime = 1000;    /* The default - 1 second */
  236.  
  237. static void
  238. run_game_proc(client_data, id)
  239. XtPointer client_data;
  240. XtIntervalId *id;
  241. {
  242.     Side *side;
  243.     Map *map;
  244.     Unit *unit;
  245.     unsigned long interval;
  246.     Boolean PlayersStillMoving = beforestart = False;
  247.     static enum { slow, fast } last_message = slow;
  248.  
  249.     /* Run the kernel itself. */
  250.     run_game(1);
  251.     /* See if any human's are still moving... */
  252.     for_all_sides(side) {
  253.     if (active_display(side)) {
  254.         /* Is this player still moving? */
  255.         if (side->ingame &&
  256.         side_has_display(side) &&
  257.         !side->finishedturn &&
  258. /*        !side_has_ai(side) &&  */
  259.             side_initacp(side) > 0 &&
  260.         side_acp_human(side) > 0) {
  261.         PlayersStillMoving = True;
  262.         }
  263.     }
  264.     }
  265.     /* If any players are still moving, then don't Timeout again until */
  266.     /* the required number of seconds have passed. */
  267.     /* Otherwise, call again quickly. */
  268.     /* interval is in milliseconds. */
  269.     if (PlayersStillMoving) {
  270.     interval = EffectiveMpTime;
  271.     if (last_message != slow) {
  272.         if (DebugG)
  273.           notify_all("Next turn; reverting back to slow machine time mode.");
  274.         last_message = slow;
  275.     }
  276.     } else {
  277.     interval = 1;
  278.     if (last_message != fast) {
  279.         if (DebugG)
  280.           notify_all("All human players are done moving; switching to fast machine player mode.");
  281.         last_message = fast;
  282.     }
  283.     }
  284.  
  285.     /* Call again in a bit... */
  286.     XtAppAddTimeOut(thisapp, interval, run_game_proc, NULL);
  287. }
  288.  
  289. static Boolean
  290. run_game_idle(clientdata)
  291. XtPointer clientdata;
  292. {
  293.     Side *side;
  294.     Map *map;
  295.     Unit *unit;
  296.  
  297.     /* See if we should jump to another unit and make it current. */
  298.     for_all_sides(side) {
  299.     if (active_display(side)) {
  300.         for_all_maps(side, map) {
  301.         if (map->curtool == movetool) {
  302.             unit = autonext_unit_inbox(side, map->curunit, map->vp);
  303.             if (unit != NULL)
  304.               set_current_unit(side, map, unit);
  305.         }
  306.         }
  307.     }
  308.     }
  309.     /* Call us again! */
  310.     return False;
  311. }
  312.  
  313. void
  314. notify_instructions()
  315. {
  316.     Obj *instructions = mainmodule->instructions, *rest;
  317.  
  318.     if (instructions != lispnil) {
  319.     if (stringp(instructions)) {
  320.         notify_all("%s", c_string(instructions));
  321.     } else if (consp(instructions)) {
  322.         for (rest = instructions; rest != lispnil; rest = cdr(rest)) {
  323.         if (stringp(car(rest))) {
  324.             notify_all("%s", c_string(car(rest)));
  325.         } else {
  326.             /* (should probably warn about this case too) */
  327.         }
  328.         }
  329.     } else {
  330.         run_warning("Instructions are of wrong type");
  331.     }
  332.     } else {
  333.     notify_all("(no instructions supplied)");
  334.     }
  335. }
  336.  
  337. /* The default (human) player is the current user on the current display. */
  338.  
  339. Player *
  340. add_default_player()
  341. {
  342.     Player *player = add_player();
  343.     
  344.     player->name = getenv("USER");
  345.     player->configname = getenv("XCONQ_CONFIG");
  346.     player->displayname = getenv("DISPLAY");
  347.     return player;
  348. }
  349.  
  350. /* An init error needs to have the command re-run. */
  351.  
  352. void
  353. low_init_error(str)
  354. char *str;
  355. {
  356.     fprintf(stderr, "Error: %s.\n", str);
  357.     fflush(stderr);
  358.     exit(1);
  359. }
  360.  
  361. /* A warning just gets displayed, no other action is taken. */
  362.  
  363. void
  364. low_init_warning(str)
  365. char *str;
  366. {
  367.     fprintf(stderr, "Warning: %s.\n", str);
  368.     fflush(stderr);
  369. }
  370.  
  371. void
  372. low_run_error(str)
  373. char *str;
  374. {
  375.     close_displays();
  376.     fprintf(stderr, "Error: %s.\n", str);
  377.     fflush(stderr);
  378.     fprintf(stderr, "Saving the game...");
  379.     write_entire_game_state(saved_game_filename());
  380.     fprintf(stderr, " done.\n");
  381.     exit(1);
  382. }
  383.  
  384. /* Runtime warnings are for when it's important to bug the players,
  385.    usually a problem with Xconq or a game design. */
  386.  
  387. void
  388. low_run_warning(str)
  389. char *str;
  390. {
  391.     notify_all("Warning: %s; continuing...", str);
  392. }
  393.  
  394. void
  395. init_x_signal_handlers()
  396. {
  397.     if (!Debug) {
  398.     XSetErrorHandler(handle_x_error);
  399.     XSetIOErrorHandler(handle_xio_error);
  400.     XtAppSetErrorHandler(thisapp, handle_xt_error);
  401.     }
  402. }
  403.  
  404. /* Handlers for X catastrophes attempt to do a save first. */
  405.  
  406. static int
  407. handle_x_error (dpy, evt)
  408. Display *dpy;
  409. XErrorEvent *evt;
  410. {
  411.     static int num_errors = 0;
  412.     char buf[BUFSIZE];
  413.  
  414.     XGetErrorText(dpy, evt->error_code, buf, BUFSIZE);
  415.     fprintf(stderr, "\nX error on display %s: %s\n", DisplayString(dpy), buf);
  416.     if (++num_errors >= 10) {
  417.         printf("\nX error: trying emergency save!\n");
  418.     /* Note that if the save fails too, we're totally hosed. */
  419.     /* (should use configurable name here) */
  420.         write_entire_game_state("ack!.xconq");
  421.     abort();
  422.     }
  423.     return 0;
  424. }
  425.  
  426. static int
  427. handle_xio_error (dpy)
  428. Display *dpy;
  429. {
  430.     fprintf(stderr, "\nX IO error on display %s: trying emergency save!\n",
  431.        DisplayString(dpy));
  432.     write_entire_game_state("ack!.xconq");
  433.     abort();
  434.     return 0;
  435. }
  436.  
  437. static void
  438. handle_xt_error(msg)
  439. String msg;
  440. {
  441.     fprintf(stderr, "Xt error: %s\n", msg);
  442.     /* Get a core dump to debug with. */
  443.     abort();
  444. }
  445.  
  446. /* Reading is usually pretty fast, so don't do anything special here. */
  447.  
  448. void
  449. announce_read_progress()
  450. {
  451. }
  452.  
  453. /* Announce the start of a time-consuming computation. */
  454.  
  455. void
  456. announce_lengthy_process(msg)
  457. char *msg;
  458. {
  459.     n_seconds_elapsed(0);
  460.     announcemsg = copy_string(msg);
  461.     if (announcemsg) {
  462.     printf("%s;", announcemsg);
  463.     announcemsg = NULL;
  464.     fflush(stdout);
  465.     announced = TRUE;
  466.     }
  467. }
  468.  
  469. /* Announce the making of progress on the computation. */
  470.  
  471. void
  472. announce_progress(percentdone)
  473. int percentdone;
  474. {
  475.     if (n_seconds_elapsed(2)) {
  476.     printf(" %d%%,", percentdone);
  477.     fflush(stdout);
  478.     announced = TRUE;
  479.     }
  480. }
  481.  
  482. /* Announce the end of the time-consuming computation. */
  483.  
  484. void
  485. finish_lengthy_process()
  486. {
  487.     if (announced) {
  488.     printf(" done.\n");
  489.     announced = FALSE;
  490.     }
  491. }
  492.  
  493. /* All update_xxx_display callbacks are here. */
  494.  
  495. /* Draw an individual detailed hex, as a row of one, on all maps. */
  496.  
  497. void
  498. update_cell_display(side, x, y, rightnow)
  499. Side *side;
  500. int x, y, rightnow;
  501. {
  502.     Map *map;
  503.  
  504.     if (active_display(side)) {
  505.     for_all_maps(side, map) {
  506.         draw_row(side, map, x, y, 1, TRUE);
  507.         if (map->curunit && map->curunit->x == x && map->curunit->y == y) {
  508.         draw_current(side, map);
  509.         }
  510.     }
  511.     if (rightnow)
  512.       flush_output(side);
  513.     }
  514. }
  515.  
  516. /* The kernel calls this to update info about the given side. */
  517.  
  518. void
  519. update_side_display(side, side2, rightnow)
  520. Side *side, *side2;
  521. int rightnow;
  522. {
  523.     Map *map;
  524.  
  525.     if (active_display(side)) {
  526.     for_all_maps(side, map) {
  527.         draw_side_info(side, map, side2);
  528.         if (endofgame) {
  529.         side->ui->mayseeall = TRUE;
  530.         update_controls(side, map);
  531.         }
  532.     }
  533.     if (rightnow)
  534.       flush_output(side);
  535.     }
  536. }
  537.  
  538. /* The kernel calls this to update info about the given unit. */
  539.  
  540. void
  541. update_unit_display(side, unit, rightnow)
  542. Side *side;
  543. Unit *unit;
  544. int rightnow;
  545. {
  546.     Map *map;
  547.  
  548.     if (active_display(side) && unit != NULL) {
  549.     if (inside_area(unit->x, unit->y)) {
  550.         update_cell_display(side, unit->x, unit->y, rightnow);
  551.     }
  552.     /* Redraw any/all info about unit and its side. */
  553.     for_all_maps(side, map) {
  554.         draw_map_info(side, map);
  555.         draw_side_info(side, map, unit->side);
  556.         update_unit_type_list(side, map, unit->type);
  557.     }
  558.     if (rightnow)
  559.       flush_output(side);
  560.     }
  561. }
  562.  
  563. void
  564. update_unit_acp_display(side, unit, rightnow)
  565. Side *side;
  566. Unit *unit;
  567. int rightnow;
  568. {
  569.     if (active_display(side)) {
  570.     }
  571. }
  572.  
  573. void
  574. update_action_result_display(side, unit, rslt, rightnow)
  575. Side *side;
  576. Unit *unit;
  577. int rslt, rightnow;
  578. {
  579.     Action *action;
  580.     Unit *unit2;
  581.     char *unit2handle = NULL;
  582.  
  583.     if (active_display(side)) {
  584.     action = &(unit->act->nextaction);
  585.     switch (rslt) {
  586.       case A_ANY_DONE:
  587.         /* (anything worthwhile to show?) */
  588.         break;
  589.       case A_FIRE_AT_TOO_FAR:
  590.         unit2 = find_unit(action->args[0]);
  591.         if (unit2) {
  592.         unit2handle = unit_handle(side, unit2);
  593.         if (unit2handle)
  594.             unit2handle = strdup(unit2handle);
  595.             notify(side, "Distance to %s (%d) is out of range (%d) of %s.",
  596.             unit2handle ? unit2handle : "target",
  597.             distance(unit->x, unit->y, unit2->x, unit2->y),
  598.             u_range(unit->type),
  599.             unit_handle(side, unit));
  600.         if (unit2handle)
  601.             free(unit2handle);
  602.         } else {
  603.         notify(side, "That is out of range of your %s.",
  604.             unit_handle(side, unit));
  605.         }
  606.         break;
  607.       default:
  608.         break;
  609.     }
  610.     if (Debug) {
  611.         notify(side, "%s %s %s!", unit_desig(unit),
  612.            action_desig(action), hevtdefns[rslt].name);
  613.     }
  614.     }
  615. }
  616.  
  617. /* The kernel calls this to update the global game state. */
  618.  
  619. void
  620. update_turn_display(side, rightnow)
  621. Side *side;
  622. int rightnow;
  623. {
  624.     int u;
  625.     Map *map;
  626.  
  627.     if (active_display(side)) {
  628.     for_all_maps(side, map) {
  629.         draw_game_state(side, map);
  630.         /* update view in panner */
  631.         draw_view_in_panner(side, map);
  632.         for_all_unit_types(u) {
  633.         update_unit_type_list(side, map, u);
  634.         }
  635.     }
  636.     if (rightnow)
  637.       flush_output(side);
  638.     }
  639. }
  640.  
  641. void
  642. update_action_display(side, rightnow)
  643. Side *side;
  644. int rightnow;
  645. {
  646.     Map *map;
  647.  
  648.     if (active_display(side)) {
  649.     /* show state of actions */
  650.     if (rightnow)
  651.       flush_output(side);
  652.     }
  653. }
  654.  
  655. void
  656. update_event_display(side, hevt, rightnow)
  657. Side *side;
  658. HistEvent *hevt;
  659. int rightnow;
  660. {
  661.     int mayseeall, u, uid;
  662.     Unit *unit;
  663.     Side *side2;
  664.     Map *map;
  665.  
  666.     if (active_display(side)) {
  667.     switch (hevt->type) {
  668.       case H_SIDE_LOST:
  669.         if (hevt->data[0] == side_number(side)) {
  670.         notify(side, "You lost!");
  671.         } else {
  672.         notify(side, "%s lost!", side_desig(side_n(hevt->data[0])));
  673.         }
  674.         break;
  675.       case H_SIDE_WON:
  676.         if (hevt->data[0] == side_number(side)) {
  677.         notify(side, "You won!");
  678.         } else {
  679.         notify(side, "%s won!", side_desig(side_n(hevt->data[0])));
  680.         }
  681.         break;
  682.       case H_GAME_ENDED:
  683.         notify(side, "The game is over!");
  684.         /* Enable us to see everything on the map accurately. */
  685.         side->ui->mayseeall = TRUE;
  686.         for_all_maps(side, map) {
  687.         update_controls(side, map);
  688.         }
  689.         break;
  690.       case H_UNIT_COMPLETED:
  691.         side2 = side_n(hevt->data[0]);
  692.         unit = find_unit(hevt->data[1]);
  693.         if (unit != NULL) {
  694.         if (side2 == side) {
  695.             notify(side, "You completed %s.",
  696.                unit_handle(side, unit));
  697.         } else {
  698.             notify(side, "%s completed %s.",
  699.                side_desig(side2), unit_handle(side2, unit));
  700.         }
  701.         }
  702.         break;
  703.       case H_UNIT_CREATED:
  704.         side2 = side_n(hevt->data[0]);
  705.         unit = find_unit(hevt->data[1]);
  706.         if (unit != NULL) {
  707.         if (side2 == side) {
  708.             notify(side, "You created %s.",
  709.                unit_handle(side, unit));
  710.         } else {
  711.             notify(side, "%s created %s.",
  712.                side_desig(side2), unit_handle(side2, unit));
  713.         }
  714.         }
  715.         break;
  716.       default:
  717.         /* No special display desired. */
  718.         break;
  719.     }
  720.     if (rightnow)
  721.       flush_output(side);
  722.     }
  723. }
  724.  
  725. void
  726. update_fire_at_display(side, unit, unit2, m, rightnow)
  727. Side *side;
  728. Unit *unit, *unit2;
  729. int m, rightnow;
  730. {
  731.     int i, sx1, sy1, sw1, sh1, sx2, sy2, sw2, sh2, dx, dy, xx, yy;
  732.     int startticks, innerticks;
  733.     char *xxx, extrabuf[BUFSIZE];
  734.     Map *map;
  735.  
  736.     if (active_display(side)) {
  737.     i = 0;
  738.     for_all_maps(side, map) {
  739.         x_xform_unit(side, map, unit, &sx1, &sy1, &sw1, &sh1);
  740.         x_xform_unit(side, map, unit2, &sx2, &sy2, &sw2, &sh2);
  741.         /* Offset to draw lines from the middle of the units' images. */
  742.         sx1 += sw1 / 2;  sy1 += sh1 / 2;
  743.         sx2 += sw2 / 2;  sy2 += sh2 / 2;
  744.         /* Draw one segment of a line between the units. */
  745.         dx = (sx2 - sx1) / 4;  dy = (sy2 - sy1) / 4;
  746.         xx = sx1 + ((i / 2) % 4) * dx;  yy = sy1 + ((i / 2) % 4) * dy;
  747.         /* (should actually draw something) */
  748.         ++i;
  749.     }
  750.     xxx = unit_handle(side, unit);
  751.     strcpy(extrabuf, xxx);
  752.     notify(side, "%s fired at %s!",
  753.            extrabuf, unit_handle(side, unit2));
  754.     }
  755. }
  756.  
  757. /* This is for animation of fire-into actions. */
  758.  
  759. void
  760. update_fire_into_display(side, unit, x, y, z, m, rightnow)
  761. Side *side;
  762. Unit *unit;
  763. int x, y, z, m, rightnow;
  764. {
  765. }
  766.  
  767. /* Updates to clock need to be sure that display changes immediately. */
  768.  
  769. void
  770. update_clock_display(side, rightnow)
  771. Side *side;
  772. int rightnow;
  773. {
  774.     Map *map;
  775.  
  776.     if (active_display(side)) {
  777.     for_all_maps(side, map) {
  778.         draw_game_clocks(side, map);
  779.     }
  780.     if (rightnow)
  781.       flush_output(side);
  782.     }
  783. }
  784.  
  785. void
  786. update_message_display(side, sender, str, rightnow)
  787. Side *side, *sender;
  788. char *str;
  789. int rightnow;
  790. {
  791.     Map *map;
  792.  
  793.     if (active_display(side)) {
  794.     notify(side, "From %s: \"%s\"",
  795.            (sender != NULL ? short_side_title(sender) : "<anon>"), str);
  796.     if (rightnow)
  797.       flush_output(side);
  798.     }
  799. }
  800.  
  801. void
  802. update_all_progress_displays(str, s)
  803. char *str;
  804. int s;
  805. {
  806. }
  807.  
  808. void
  809. flush_display_buffers(side)
  810. Side *side;
  811. {
  812.     if (active_display(side)) {
  813.     flush_output(side);
  814.     }
  815. }
  816.  
  817. void
  818. update_everything()
  819. {
  820.     Side *side;
  821.  
  822.     for_all_sides(side) {
  823.     if (active_display(side)) {
  824.         /* (update all maps?) */
  825.     }
  826.     }
  827. }
  828.  
  829. /* Close all displays that are still open. */
  830.  
  831. void
  832. close_displays()
  833. {
  834.     Side *side;
  835.  
  836.     for_all_sides(side) {
  837.     if (active_display(side))
  838.       close_display(side);
  839.     }
  840. }
  841.  
  842. /* This routine should be called before any sort of normal exit. */
  843.  
  844. void
  845. exit_xconq(side)
  846. Side *side;
  847. {
  848.     /* (should this really happen first?) */
  849.     close_displays();
  850.     /* maybe record in scorefile */
  851.     printf("%s\n", exit_commentary(side));
  852.     exit(0);
  853. }
  854.  
  855. void
  856. printlisp(obj)
  857. Obj *obj;
  858. {
  859.     fprintlisp(stdout, obj);
  860. }
  861.  
  862. int
  863. #ifdef __STDC__
  864. schedule_movie(Side *side, enum movie_type movie, ...)
  865. #else
  866. schedule_movie(side, movie)
  867. Side *side;
  868. enum movie_type movie;
  869. #endif
  870. {
  871.     return FALSE;
  872. }
  873.  
  874. void
  875. play_movies(sidemask)
  876. SideMask sidemask;
  877. {
  878. }
  879.  
  880. /* Get rid of extra input events, generally because the situation has
  881.    changed drastically and they are no longer of interest.  This routine
  882.    has relatively few valid uses. */
  883.  
  884. void
  885. flush_input(side)
  886. Side *side;
  887. {
  888.     DGprintf("doing an input flush\n");
  889.     if (active_display(side)) {
  890.     XSync(side->ui->dpy, TRUE);  
  891.     }
  892. }
  893.  
  894. /* Build a X-compatible widget name, by replacing non-alphanumerics with
  895.    underscores. */
  896.  
  897. void
  898. build_name(name, first, second)
  899. char *name, *first, *second;
  900. {
  901.     char *ch;
  902.  
  903.     strcpy(name, first);
  904.     strcat(name, second);
  905.     for (ch = name; *ch != '\0'; ++ch) {
  906.     if (!isalnum(*ch))
  907.       *ch = '_';
  908.     }
  909. }
  910.  
  911. #ifdef DEBUGGING
  912.  
  913. /* Set all the window backgrounds, borders, GCs, etc, usually to reflect
  914.    inversion of foreground and background. */
  915.  
  916. void
  917. reset_color_state(side)
  918. Side *side;
  919. {
  920.     XGCValues values;
  921.     unsigned int gcmask = GCForeground | GCBackground;
  922.  
  923.     /* Set the GCs. */
  924.     values.foreground = side->ui->fgcolor;
  925.     values.background = side->ui->bgcolor;
  926.     XChangeGC(side->ui->dpy, side->ui->gc, gcmask, &values);
  927.     XChangeGC(side->ui->dpy, side->ui->textgc, gcmask, &values);
  928.     XChangeGC(side->ui->dpy, side->ui->ltextgc, gcmask, &values);
  929.     XChangeGC(side->ui->dpy, side->ui->unitgc, gcmask, &values);
  930. }
  931.  
  932. void
  933. reset_window_colors(side, win)
  934. Side *side;
  935. Window win;
  936. {
  937.     XSetWindowBackground(side->ui->dpy, win, side->ui->bgcolor);
  938.     XSetWindowBorder(side->ui->dpy, win, side->ui->fgcolor);
  939. }
  940.  
  941. #endif /* DEBUGGING */
  942.  
  943. /* Completely redo all windows, making no assumptions about appearance.
  944.    This is a last-gasp measure, most redrawing should be restricted
  945.    to only the directly affected windows.  Also this shouldn't be
  946.    done without the user's permission, since it will blow away impending
  947.    input. */
  948.  
  949. void
  950. redraw(side)
  951. Side *side;
  952. {
  953.     draw_all_maps(side);
  954.     /* (should do popups also?) */
  955.     flush_output(side);
  956.     flush_input(side);
  957. }
  958.  
  959. /* Trivial abstraction - sometimes other routines want to ensure all output
  960.    so far is actually on the screen and not being buffered up somewhere. */
  961.  
  962. void
  963. flush_output(side) 
  964. Side *side; 
  965. {  
  966.     XFlush(side->ui->dpy);  
  967. }
  968.  
  969. /* Beep the beeper! */
  970.  
  971. void
  972. beep(side)
  973. Side *side;
  974. {
  975.     XBell(side->ui->dpy, side->ui->screen);
  976. }
  977.  
  978. /* Send a message to everybody who's got a screen. */
  979.  
  980. #ifdef __STDC__
  981. void
  982. notify_all(char *fmt, ...)
  983. #else
  984. void
  985. notify_all(fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9)
  986. char *fmt;
  987. long a1, a2, a3, a4, a5, a6, a7, a8, a9;
  988. #endif
  989. {
  990.     char tmpnbuf[BUFSIZE];          /* tmp buf for notices only. */
  991.     Side *side;
  992.  
  993.     for_all_sides(side) {
  994.     if (active_display(side)) {
  995. #ifdef __STDC__
  996.         {
  997.         va_list ap;
  998.  
  999.         va_start(ap, fmt);
  1000.         vsprintf(tmpnbuf, fmt, ap);
  1001.         va_end(ap);
  1002.         }
  1003. #else
  1004.         sprintf(tmpnbuf, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9);
  1005. #endif
  1006.         low_notify(side, tmpnbuf);
  1007.     }
  1008.     }
  1009. }
  1010.  
  1011. #ifdef __STDC__
  1012. void
  1013. notify(Side *side, char *fmt, ...)
  1014. #else
  1015. void
  1016. notify(side, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9)
  1017. Side *side;
  1018. char *fmt;
  1019. long a1, a2, a3, a4, a5, a6, a7, a8, a9;
  1020. #endif
  1021. {
  1022.     char tmpnbuf[BUFSIZE];
  1023.  
  1024.     if (active_display(side)) {
  1025. #ifdef __STDC__
  1026.         {
  1027.         va_list ap;
  1028.  
  1029.         va_start(ap, fmt);
  1030.         vsprintf(tmpnbuf, fmt, ap);
  1031.         va_end(ap);
  1032.      }
  1033. #else
  1034.     sprintf(tmpnbuf, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9);
  1035. #endif
  1036.     low_notify(side, tmpnbuf);
  1037.     }
  1038. }
  1039.  
  1040. void
  1041. low_notify(side, str)
  1042. Side *side;
  1043. char *str;
  1044. {
  1045.     char tmpnbuf[BUFSIZE];
  1046.     Map *map;
  1047.  
  1048.     if (active_display(side)) {
  1049.     strcpy(tmpnbuf, str);
  1050.     /* Always capitalize first char of notice. */
  1051.     if (islower(tmpnbuf[0]))
  1052.       tmpnbuf[0] = toupper(tmpnbuf[0]);
  1053.     /* Paste into a subwindow of each map window. */
  1054.     for_all_maps(side, map) {
  1055.         textw_printf(map->history, "%s\n", tmpnbuf);
  1056.     }
  1057.     DGprintf("To %s: %s\n", side_desig(side), tmpnbuf);
  1058.     }
  1059. }
  1060.  
  1061. /* General text drawer. */
  1062.  
  1063. void
  1064. draw_text(side, win, x, y, str, color)
  1065. Side *side;
  1066. Window win;
  1067. int x, y, color;
  1068. char *str;
  1069. {
  1070.     XFontStruct *font = side->ui->textfont;
  1071.     GC gc = side->ui->textgc;
  1072.     Display *dpy = side->ui->dpy;
  1073.  
  1074.     y += font->max_bounds.ascent;
  1075.     XSetForeground(dpy, gc, color);
  1076.     /* If "color" is bg, assume we want reverse video. */
  1077.     XSetBackground(dpy, gc, (color == side->ui->bgcolor ? side->ui->fgcolor : side->ui->bgcolor));
  1078.     XDrawImageString(dpy, win, gc, x, y, str, strlen(str));
  1079. #ifdef STUPIDFLUSH
  1080.     XFlush(dpy);
  1081. #endif STUPIDFLUSH
  1082. }
  1083.  
  1084. /* Frequently-called routine to draw text in the foreground color. */
  1085.  
  1086. void
  1087. draw_fg_text(side, win, x, y, str)
  1088. Side *side;
  1089. Window win;
  1090. int x, y;
  1091. char *str;
  1092. {
  1093.     draw_text(side, win, x, y, str, side->ui->fgcolor);
  1094. }
  1095.  
  1096. XawTextPosition
  1097. widget_text_length(w)
  1098. Widget w;
  1099. {
  1100.     return XawTextSourceScan (XawTextGetSource (w),
  1101.                   (XawTextPosition) 0,
  1102.                    XawstAll, XawsdRight, 1, TRUE);
  1103. }
  1104.  
  1105. #if 0
  1106. void
  1107. move_caret_to_start(w)
  1108. Widget w;
  1109. {
  1110.     XawTextSetInsertionPoint(w, (XawTextPosition) 0);
  1111. }
  1112. #endif
  1113.  
  1114. void
  1115. move_caret_to_end(w)
  1116. Widget w;
  1117. {
  1118.     XawTextPosition pos;
  1119.  
  1120.     pos = widget_text_length(w);
  1121.     XawTextSetInsertionPoint(w, pos);
  1122. }
  1123.  
  1124. static int count_lines PROTO ((Widget w));
  1125.  
  1126. static int
  1127. count_lines(w)
  1128. Widget w;
  1129. {
  1130.     int    nlines;
  1131.     XawTextPosition    pos, len;
  1132.  
  1133.     len = widget_text_length(w);
  1134.  
  1135.     nlines = 0;
  1136.     pos = 0;
  1137.     while ((pos = XawTextSourceScan(XawTextGetSource(w),
  1138.                     pos, XawstEOL, XawsdRight, 1, TRUE))
  1139.        != XawTextSearchError) {
  1140.     nlines++;
  1141.     if (pos >= len)
  1142.       break;
  1143.     }
  1144.     return nlines;
  1145. }
  1146.  
  1147. static void drop_lines_from_top PROTO ((Widget w, int num));
  1148.  
  1149. static void
  1150. drop_lines_from_top(w, num)
  1151. Widget w;
  1152. int num;
  1153. {
  1154.     int    nlines;
  1155.     XawTextPosition pos, len;
  1156.     XawTextBlock tb;
  1157.     XawTextEditType et;
  1158.  
  1159.     len = widget_text_length(w);
  1160.  
  1161.     nlines = num;
  1162.     pos = 0;
  1163.     while (nlines &&
  1164.        (pos = XawTextSourceScan(XawTextGetSource(w),
  1165.                     pos, XawstEOL, XawsdRight, 1, TRUE))
  1166.        != XawTextSearchError) {
  1167.     nlines--;
  1168.     if (pos >= len)
  1169.       break;
  1170.     }
  1171.     
  1172.     if (pos == XawTextSearchError)
  1173.       return;
  1174.     
  1175.     tb.firstPos = 0;
  1176.     tb.length = 0;
  1177.     tb.ptr = NULL;
  1178.     tb.format = FMT8BIT;
  1179.  
  1180.     /* Can we write to it? */
  1181.     nargs = 0;
  1182.     XtSetArg(tmpargs[nargs], XtNeditType, &et);  nargs++;
  1183.     XtGetValues(w, tmpargs, nargs);
  1184.  
  1185.     if (et == XawtextRead) {
  1186.     /* Make it writable. */
  1187.     nargs = 0;
  1188.     XtSetArg(tmpargs[nargs], XtNeditType, XawtextEdit);  nargs++;
  1189.     XtSetValues(w, tmpargs, nargs);
  1190.     }
  1191.     
  1192.     XawTextReplace(w, (XawTextPosition) 0, pos, &tb);
  1193.  
  1194.     if (et == XawtextRead) {
  1195.     /* Set it back. */
  1196.     nargs = 0;
  1197.     XtSetArg(tmpargs[nargs], XtNeditType, et);  nargs++;
  1198.     XtSetValues(w, tmpargs, nargs);
  1199.     }
  1200.  
  1201.     move_caret_to_end(w);
  1202. }
  1203.  
  1204. void
  1205. #ifdef __STDC__
  1206. textw_printf(const Widget w, const char *fmt, ...)
  1207. #else
  1208. textw_printf(w, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9)
  1209. Widget w;
  1210. char *fmt;
  1211. long a1, a2, a3, a4, a5, a6, a7, a8, a9;
  1212. #endif
  1213. {
  1214.     XawTextBlock tb;
  1215.     XawTextPosition ins_point;
  1216.     XawTextEditType et;
  1217.     int nlines;
  1218.     char buf[BUFSIZ];
  1219.  
  1220. #ifdef __STDC__
  1221.     {
  1222.     va_list vl;
  1223.  
  1224.     va_start(vl, fmt);
  1225.     (void) vsprintf(buf, fmt, vl);
  1226.     va_end(vl);
  1227.     }
  1228. #else
  1229.     sprintf(buf, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9);
  1230. #endif
  1231.     if (w == (Widget) NULL) {
  1232.     printf("%s", buf);
  1233.     return;
  1234.     }
  1235.  
  1236.     tb.ptr = buf;
  1237.     tb.length = strlen(buf);
  1238.     tb.format = FMT8BIT;
  1239.     tb.firstPos = 0;
  1240.  
  1241.     /* Can we write to it? */
  1242.     nargs = 0;
  1243.     XtSetArg(tmpargs[nargs], XtNeditType, &et);  nargs++;
  1244.     XtGetValues(w, tmpargs, nargs);
  1245.  
  1246.     if (et == XawtextRead) {
  1247.     /* Make it writable. */
  1248.     nargs = 0;
  1249.     XtSetArg(tmpargs[nargs], XtNeditType, XawtextEdit);  nargs++;
  1250.     XtSetValues(w, tmpargs, nargs);
  1251.     }
  1252.     move_caret_to_end(w);
  1253.     ins_point = widget_text_length(w);
  1254.     XawTextReplace(w, ins_point, ins_point, &tb);
  1255.     move_caret_to_end(w);
  1256.     if (et == XawtextRead) {
  1257.     /* Set it back. */
  1258.     nargs = 0;
  1259.     XtSetArg(tmpargs[nargs], XtNeditType, et);  nargs++;
  1260.     XtSetValues(w, tmpargs, nargs);
  1261.     }
  1262.     nlines = count_lines(w);
  1263.     /* Need to make this a resource. */
  1264. #define maxlines 256
  1265.     if (nlines > maxlines)
  1266.       drop_lines_from_top(w, nlines - maxlines);
  1267. #undef maxlines
  1268. }
  1269.  
  1270. #if 0
  1271. void textw_iprintf PROTO ((const Widget w, const char *fmt, ...));
  1272.  
  1273. void
  1274. #ifdef __STDC__
  1275. textw_iprintf(const Widget w, const char *fmt, ...)
  1276. #else
  1277. textw_iprintf(w, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9)
  1278. Widget w;
  1279. char *fmt;
  1280. long a1, a2, a3, a4, a5, a6, a7, a8, a9;
  1281. #endif
  1282. {
  1283.     XawTextBlock tb;
  1284.     XawTextPosition    ins_point;
  1285.     XawTextEditType    et;
  1286.     char buf[BUFSIZ];
  1287.  
  1288. #ifdef __STDC__
  1289.     {
  1290.     va_list        vl;
  1291.  
  1292.     va_start(vl, fmt);
  1293.     (void) vsprintf(buf, fmt, vl);
  1294.     va_end(vl);
  1295.     }
  1296. #else
  1297.     sprintf(buf, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9);
  1298. #endif
  1299.     tb.ptr = buf;
  1300.     tb.length = strlen(buf);
  1301.     tb.format = FMT8BIT;
  1302.     tb.firstPos = 0;
  1303.  
  1304.     /* Can we write to it? */
  1305.     nargs = 0;
  1306.     XtSetArg(tmpargs[nargs], XtNeditType, &et);  nargs++;
  1307.     XtGetValues(w, tmpargs, nargs);
  1308.  
  1309.     if (et == XawtextRead) {
  1310.     /* Make it writable. */
  1311.     nargs = 0;
  1312.     XtSetArg(tmpargs[nargs], XtNeditType, XawtextEdit);  nargs++;
  1313.     XtSetValues(w, tmpargs, nargs);
  1314.     }
  1315.     ins_point = XawTextGetInsertionPoint(w);
  1316.     XawTextReplace(w, ins_point, ins_point, &tb);
  1317.     if (et == XawtextRead) {
  1318.     /* Set it back. */
  1319.     nargs = 0;
  1320.     XtSetArg(tmpargs[nargs], XtNeditType, et);  nargs++;
  1321.     XtSetValues(w, tmpargs, nargs);
  1322.     }
  1323. }
  1324. #endif
  1325.  
  1326. #if 0
  1327. void
  1328. EraseTextWidget(w)
  1329. Widget w;
  1330. {
  1331.     XawTextBlock block;
  1332.  
  1333.     block.ptr = "";
  1334.     block.length = 0;
  1335.     block.firstPos = 0;
  1336.     block.format = FMT8BIT;
  1337.  
  1338.     XawTextReplace(w, 0, widget_text_length(w), &block);
  1339.     /* If this fails, too bad. */
  1340.     move_caret_to_end(w);
  1341. }
  1342. #endif
  1343.  
  1344. int
  1345. find_side_via_widget(w, sidep)
  1346. Widget w;
  1347. Side **sidep;
  1348. {
  1349.     Side *side;
  1350.  
  1351.     for_all_sides(side) {
  1352.     if (active_display(side)) {
  1353.         if (XtDisplay(w) == side->ui->dpy) {
  1354.         *sidep = side;
  1355.         return TRUE;
  1356.         }
  1357.     }
  1358.     }
  1359.     return FALSE;
  1360. }
  1361.